/*
 * Copyright (C) 2013 The original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.zapta.apps.maniana.model;

import java.util.Calendar;
import java.util.GregorianCalendar;

import com.zapta.apps.maniana.annotations.ApplicationScope;
import com.zapta.apps.maniana.model.ItemRepeat;
import com.zapta.apps.maniana.model.ItemRepeatReadOnly;
import com.zapta.apps.maniana.model.ItemRepeatMonthReadOnly;

/**
 * Represents the data on when an item should repeat (day or count of days)
 *
 * @author Elliott Mitchell
 */

@ApplicationScope
public class ItemRepeatMonth extends ItemRepeat implements ItemRepeatMonthReadOnly {

	/** Repeat based on day of week, rather than day # of month */
	private boolean mDayOfWeek;

	/** We're relative to the end of month, rather than start */
	private boolean mRelativeEnd;

	/** Number of months to add each time */
	private byte mMonths;

	public ItemRepeatMonth() {
		super();
	}

	public ItemRepeatMonth(ItemRepeatMonthReadOnly other) {
		super(other);
		mDayOfWeek = other.getRepeatDayOfWeek();
		mRelativeEnd = other.getRelativeEnd();
		mMonths = (byte)other.getRepeatMonths();
	}

	public ItemRepeatMonth(ItemModel.StoreIF IF) {
		super(IF);
	}

	@Override
	public ItemRepeatType getType() {
		return ItemRepeatType.MONTH;
	}

	@Override
	public boolean getRepeatDayOfWeek() {
		return mDayOfWeek;
	}
	public void setRepeatDayOfWeek(boolean repeatDayOfWeek) {
		mDayOfWeek=repeatDayOfWeek;
	}

	@Override
	public boolean getRelativeEnd() {
		return mRelativeEnd;
	}
	public void setRelativeEnd(boolean relEnd) {
		mRelativeEnd=relEnd;
	}

	@Override
	public short getRepeatMonths() {
		return (short)((mMonths&0xFF)+1);
	}
	public void setRepeatMonths(short repeatMonths) {
		mMonths=(byte)((repeatMonths-1)&0xFF);
	}

	@Override
	public void fromStore(ItemModel.StoreIF IF) {
		super.fromStore(IF, "month");
		mDayOfWeek=IF.getBoolean("repeat_day_of_week");
		mRelativeEnd=IF.getBoolean("repeat_relative_end");
		mMonths=IF.getByte("repeat_months");
	}

	@Override
	public void toStore(ItemModel.StoreIF IF) {
		super.toStore(IF);
		IF.setString("repeat_type", "month");
		if(mDayOfWeek) IF.setBoolean("repeat_day_of_week", true);
		if(mRelativeEnd)IF.setBoolean("repeat_relative_end",true);
		if(mMonths!=0) IF.setByte("repeat_months", mMonths);
	}

	@Override
	public boolean checkDelete(ItemModel item) {
		final int old=item.getDueDate();
		final Calendar cal=new GregorianCalendar(
(old>>>ItemModel.SHIFT_YEAR)&ItemModel.MASK_YEAR,
(old>>>ItemModel.SHIFT_MONTH)&ItemModel.MASK_MONTH,
(old>>>ItemModel.SHIFT_DAY)&ItemModel.MASK_DAY);

		final byte field;

		if(mDayOfWeek) { // repeat by day of month
			final byte weekDay=(byte)cal.get(Calendar.DAY_OF_WEEK);
			cal.set(Calendar.DAY_OF_WEEK, weekDay);
			field=Calendar.DAY_OF_WEEK_IN_MONTH;
		} else field=Calendar.DAY_OF_MONTH;

		final byte val=(byte)cal.get(field);
		byte newval;

		if(mRelativeEnd) {
			final byte delta=(byte)(cal.getActualMaximum(field)-val);

			do {
				cal.add(Calendar.MONTH, (mMonths&0xFF)+1);
				newval=(byte)(cal.getActualMaximum(field)-delta);
			} while(newval<=0);

		} else {
			newval=val;
			do {
				cal.add(Calendar.MONTH, (mMonths&0xFF)+1);
			} while(cal.getActualMaximum(field)<val);
		}
		cal.set(field, newval);

		final int next=(cal.get(Calendar.YEAR)<<ItemModel.SHIFT_YEAR)|
(cal.get(Calendar.MONTH)<<ItemModel.SHIFT_MONTH)|
(cal.get(Calendar.DAY_OF_MONTH)<<ItemModel.SHIFT_DAY);

		return super.checkDelete(item, next);
	}
}

